/*============================================================================
  File:      IDMAlgorithm.Stubs.cpp
 
  Summary:   IDMAlgorithm stubs to paste into implementation file for class 
			 implementing the new algorithm

  Date:		 September 12, 2003
------------------------------------------------------------------------------
  This file is part of the Microsoft SQL Server Code Samples.
 
  Copyright (C) 2003 Microsoft Corporation.  All rights reserved.
 
This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.
 
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
============================================================================*/

////
//
// IDMAlgorithm interface
//
////


STDMETHODIMP YOUR_ALG_CLASS::Initialize( 
					/* [in] */ IDMContextServices*	in_pExeContext,
					/* [in] */ IDMAttributeSet*		in_pAttributeSet,
					/* [in] */ IDMMarginalStat*		in_pMarginalStat)
{
	// Provide initialization of the algorithm. All allocations are done
	// with the context-services interface.

	// The algorithm will probably want to cache away the attribute-set
	// interface, or alternatively, initialize some other representation
	// for the set of attributes by using this interface.

	// E.g., in the definition of YOUR_ALG_CLASS include the member:
	//
	//	CComPtr<IDMAttributeSet> _spidmattributeset;
	// 
	// and then:

	/*
	_spidmattributeset = in_pAttributeSet;
	*/

	// The marginal-statistics interface contains marginal counts for
	// each attribute in the training data. These pre-computed counts may
	// be useful to the algorithm - you save a data pass by using this
	// interface instead of scanning the data.
	
	return S_OK;
}

STDMETHODIMP YOUR_ALG_CLASS::InsertCases( 
					/* [in] */ IDMContextServices *in_pExeContext,
					/* [in] */ IDMPushCaseSet *in_pCaseSet,
					/* [in] */ ULONG in_ulNumParameters,
					/* [in] */ WCHAR *in_rgParameterNames[  ],
					/* [in] */ DMVariantPtr in_rgParameterValues[  ])
{
	// This function is the main "train the model" function. 
	// 
	// The last three inputs to the function allow algorithm-specific parameters
	// to be passed in. If the algorithm's behaviour depends on one or more such
	// parameters, you must extract the appropriate values.
	//
	// If you use the static parameter handling functionality, you can
	// extract these parameters via the static DMPARAMHANDLER member of your
	// algorithm class. Suppose, for example, that you have a type-double
	// parameter called "MY_DOUBLE" that was defined using the DECLARE_PARAMETER
	// macro. You can extract the value via:

	/*
	
	double dbl;
	
	_dmhparamhandler.GetParameterValue(in_pExeContext, dbl, L"MY_DOUBLE",
									   in_ulNumParameters,
									   in_rgParameterNames,
									   in_rgParameterValues);
	*/

	// If MY_DOUBLE has a default value and an explicit value is not pased in,
	// the GetParameterValue() will return the default value.

	//
	// The training data is provided to the algorithm by means of a callback. In
	// particular, the algorithm needs to provide a case-processor interface 
	// (IDMCaseProcessor) to the server by means of 
	//
	//		in_pCaseSet->StartCases(pIDMCaseProcessor, bRequireIDs);
	//
	// The second parameter tells the server whether or not the algorithm 
	// requires the ids associated with each case. For each case in the data, 
	// the server will call pIDMCaseProcessor->ProcessCase(), and it is up to 
	// the case processor to populate the algorithm data.
	//
	// The function in_pCaseSet->StartCases() returns once all data has been
	// provided to the algorithm. 
   
	return S_OK;
}


STDMETHODIMP YOUR_ALG_CLASS::Predict( 
    /* [in] */ IDMContextServices*	in_pContext,
    /* [in] */ DM_PREDICTION_FLAGS	in_Flags,
    /* [in] */ IDMAttributeGroup*	in_pPredAttGroup,
    /* [in] */ DM_CaseID			in_CaseID,
    /* [in] */ ULONG				in_ulCaseValues,
    /* [in] */ DM_ATTRIBUTE_VALUE*	in_rgValues,
    /* [in] */ ULONG				in_ulMaxPredictions,
    /* [in] */ ULONG				in_ulMaxStates,
    /* [out] */ DM_ATTRIBUTE_STAT**	io_prgPredictions,
    /* [out] */ ULONG*				out_pulPredictions)
{
	// This is the function used to extract predictions from your model. 
	//
	// The output variables (i.e., the variables for which you are being requested
	// to return predictions) are specified in a "group" using IDMAttributeGroup; you 
	// iterate through this group using IDMAttributeGroup::Reset() and 
	// IDMAttributeGroup::Next(). Some of the attributes in this group, however, may
	// not be allowed to be predicted, based on the input DM_PREDICTION_FLAGS in_flags.
	//
	// The known input variables are supplied in a "case" of attribute/value pairs
	// in_rgValues (this array has in_ulCaseValues elements). 
	//
	// ************* IMPORTANT NOTE: 
	// If in_rgValues[i].Attribute is equal to DM_UNSPECIFIED, you are to ignore it in the 
	// input case!!!
	//
	// The output distributions are to be placed in io_prgPredictions: if the contents of
	// this pointer is NULL, you need to allocate the appropriate amount of space for it 
	// using the in_pContext; otherwise, if io_prgPredictions is not NULL, you are guaranteed by
	// the caller that out_pulPredictions has enough space to hold all of the necessary
	// predictions.
	//
	// You should limit the output list to at most in_ulMaxPredictions, where each
	// discrete-variable prediction contains the description for at most 
	// in_ulMaxStates states.
	// 
	// The supplied DM_PREDICTION_FLAGS determine the behaviour of the call:
	//
	//	DMPF_EXCLUDE_INPUT_ATTRIBUTES			Don't predict any attribute that is explicitly
	//											specified in in_rgValues
	//
	//	DMPF_ONLY_INPUT_ATTRIBUTES				Only predict those values that are explicitly
	//											specified in in_rgValues
	//
	//	DMPF_EXCLUDE_MISSING_STATE				Don't return a prediction about the missing
	//											state
	//
	//	DMPF_ATTRIBUTE_SORT_ASCENDING			Sort io_prgPredictions in ascending order, based
	//											on the value of (in_flags | DMPF_ATT_SORT_MASK)
	//
	//	DMPF_STATE_SORT_ASCENDING				Sort the states of each prediction in ascending
	//											order, based on the value of 
	//											(in_flags | DMPF_STATE_SORT_MASK)
	//
	//	DMPF_INCLUDE_STATS						Populate the rgStateStats member for each 
	//											distribution. Similar to the io_prgPredictions passed in,
	//											if rgStateStats is NULL, you need to allocate the
	//											necessary memory; otherwise, you are guaranteed by
	//											the caller that rgStateStats contains enough space
	//											for your target distribution.
	//
	//	DMPF_INCLUDE_NODE_ID					Populate DM_ATTRIBUTE_STAT::strNodeID for each
	//											prediction. This ID is supposed to be "relevant"
	//											to the prediction that was made, and may be
	//											distinct from the "drill-through" id and/or the
	//											"mining model dimension" id. (See GetNodeIDsForCase).
	//
	//	DMPF_INCLUDE_TIME						Not used for this function.
	//
	//	DMPF_ATT_SORT_MASK						Mask for determining how to sort attributes
	//
	//	DMPF_ATT_SORT_EXIST_SUPPORT				Sort attributes by support (i.e. number
	//											of cases in the training data)
	//
	//	DMPF_ATT_SORT_EXIST_PROB				Sort attributes by posterior probability of
	//											non-missing.
	//
	//	DMPF_ATT_SORT_EXIST_ADJUST_PROB			Sort attributes by "adjusted probability". The
	//											adjusted probability is used to implement (e.g.)
	//											a penalty for popular items.
	//
	//	DMPF_STATE_SORT_MASK					Mask for determining how to sort discrete states
	//
	//	DMPF_STATE_SORT_PROB					Sort states by posterior probability
	//
	//	DMPF_STATE_SORT_SUPPORT					Sort states by support
	//
	//	DMPF_STATE_SORT_VARIANCE				Sort states by posterior variance
	//
	//	DMPF_STATE_SORT_ADJUST_PROB				Sort states by "adjusted probability"
	//
	//
	// A note on representing the posterior of a continuous distribution
	// -----------------------------------------------------------------
	// All continuous distributions are necessarily "binary continuous": there's a probability of 
	// missing vs. present, and then a conditional (continuous) distribution given that the attribute 
	// is present. The continuous posterior must is characterized (for the purposes of Predict) by
	// a mean and variance.
	//
	// Assuming the caller has requested Predict() to include statistics, the (DM_STATE_STAT*) 
	// rgStateStats member of each DM_ATTRIBUTE_STAT must be allocated to size = 2; one element 
	// specifies the distribution of the missing state, and the other element specifies both 
	// p(present) and the conditional distribution given that the attribute is present.
	// 
	// Suppose we have the following posterior distribution:
	//
	//
	// p(x)    = {    p(x is missing)                             if x is missing
    //				  p(x not missing) * p(x | x not missing)     if x is not missing
	//		     }
	//
	// and we want to populate a DM_ATTRIBUTE_STAT called dmattributestat. First, we would 
	// allocate two members for dmattributestat.rgStateStats. Then,
	//
	// // Specify p(x is not missing) and p(x | x is not missing) using a DM_STATE_STAT: 
	// // the posterior mean is specified using the Value member in this case
	// dmattributestat.rgStateStats[1].Value = DmstatevalueContinuous(/* posterior mean (given not missing)*/); 
	// dmattributestat.rgStateStats[1].dblVariance = // posterior variance (given not missing)
	// dmattributestat.rgStateStats[1].dblProbability = // p(x is not missing)
	// dmattributestat.rgStateStats[1].dblSupport =		// Number cases where x is not missing
	// dmattributestat.rgStateStats[1].dblProbabilityVariance = 0; // Variance of p(x is not missing). Not used.
	//
	// // Specify p(x is missing): put missing as the Value, and specify the probability of missing.
	// dmattributestat.rgStateStats[0].Value = DmstatevalueContinuous(dblMissing); 
	// dmattributestat.rgStateStats[0].dblProbability = // p(x is missing)
	// dmattributestat.rgStateStats[0].dblSupport =		// Number cases where x is missing
	// dmattributestat.rgStateStats[0].dblVariance = 0; // N/A: just set to 0
	// dmattributestat.rgStateStats[0].dblProbabilityVariance = 0; // Variance of p(x is missing). Not used.
	//
	
	return S_OK;
}

STDMETHODIMP YOUR_ALG_CLASS::GetNodeIDsForCase( 
        /* [in] */ IDMContextServices*	in_pContext,
        /* [in] */ DM_NODE_FLAGS		in_Flags,
        /* [in] */ DM_CaseID			in_CaseID,
        /* [in] */ ULONG				in_ulCaseValues,
        /* [in] */ DM_ATTRIBUTE_VALUE*	in_rgValues,
        /* [in] */ ULONG				in_ulMaxNodeIDs,
        /* [out] */ DMStringPtr**		io_prgNodeIDs,
        /* [out] */ ULONG*				out_pulNodeIDs)
{
	// This is the main function used for the "DM Dimension" slicing and "drill
	// through". The idea is that your model implements a mapping from cases into 
	// "node ids". For "DM Dimension" mode, the mapping results, for each case, 
	// in exactly one node id for each output variable. In non-DM-Dimension mode, 
	// there can be multiple node ids for each output variable.
	
	// The input DM_NODE_FLAGS specifies that you are to implement the DM-dimension
	// mapping by setting the DMNF_DIMENSION_CONTENT_NODE bits.

	// The node ids returned by this function are the unique names
	// (GetNodeIDFromUniqueName) used by IDMAlgorithmNavigation.


	return S_OK;
}

STDMETHODIMP YOUR_ALG_CLASS::GetNavigator( 
    /* [in] */	IDMContextServices *in_pContext,
    /* [in] */	BOOL in_fDimension,
    /* [out] */ IDMAlgorithmNavigation **out_ppDAGNav)
{
	// This function returns an algorithm-navigation interface. 
	// Several instances of this interface may be active concurrently 
	// over a single algorithm instance, so you want to implement
	// the navigation with a separate class.
	//
	// Assuming that the IDMAlgorithmMetadata interface is implemented
	// using static member functions, the following check will be 
	// appropriate. Otherwise, you should delete this code.

	HRESULT hr = S_OK;

/*	BOOL bSupportDMDimensions;

	hr = GetSupportsDMDimensions(in_pContext, &bSupportDMDimensions);

	RETURN_ON_FAIL(hr);

	if (in_fDimension && !bSupportDMDimensions)
	{
		// The algorithm has declared that it does not support
		// DM dimensions, yet the caller is attempting to get
		// a navigator appropriate for DM dimensions.

		return E_FAIL;
	}
*/	

	// Create an instance of your navigation class and return the
	// desired interface. For example:

	/*

	CComObject<YOUR_NAVIGATION_CLASS>*	pNavClass;

	hr = CComObject<YOUR_NAVIGATION_CLASS>::CreateInstance(&pNavClass);
		
	if (SUCCEEDED(hr) && pNavClass)
	{
		return pNavClass->QueryInterface(__uuidof(IDMAlgorithmNavigation), 
										 (void**) out_ppDAGNav);
	}
	*/

	return hr;
}
	
STDMETHODIMP	YOUR_ALG_CLASS::GetSampleCaseSet( 
        /* [in] */ IDMContextServices*	in_pContext,
        /* [in] */ DMString*			in_pstrNodeID,
        /* [out] */ IDMPullCaseSet**	out_ppCaseSet)
{
	// This function is supposed to return a set of cases that are randomly
	// sampled in some way that is "appropriate" for a particular node in the 
	// model. Depending on your model, this may or may not make any sense.

	return S_OK;
}


STDMETHODIMP YOUR_ALG_CLASS::GetTrainingParameterActualValue( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [in] */ ULONG in_iParameter,
    /* [in] */ DMVariantPtr io_pParameterValue)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::HasFeatureSelection( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [out] */ BOOL *out_pHasFS)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::GetFeatureSelectedAttributes( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [out] */ ULONG *out_pCount,
    /* [out] */ DM_Attribute **out_prgFeatureSelectedAttributes,
    /* [out] */ DM_ATTRIBUTE_FLAGS **out_prgFSAttributeFlags)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::GetAttributeFeatureSelectionFlags( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [in] */ DM_Attribute in_nAttr,
    /* [out] */ DM_ATTRIBUTE_FLAGS *out_pAttFlags)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::GetProcessingMemoryEstimate( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [out] */ LONG *out_pEstimate)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


void STDMETHODCALLTYPE YOUR_ALG_CLASS::UpdateMemoryEstimate( 
    /* [in] */ LONG in_lEstimateChange)
{ 
	return; 
}


LONG STDMETHODCALLTYPE YOUR_ALG_CLASS::ReservedMemoryEstimate( void )
{ 
	return 0; 
}


STDMETHODIMP YOUR_ALG_CLASS::RenderPMMLContent( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [in] */ ISAXContentHandler *in_pXMLWriter)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::PreInitializeForPMML21Parsing( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [in] */ IDMAttributeSet *in_pAttributeSet)
{ 
	HRESULT hr = S_OK;
	return hr; 
}


STDMETHODIMP YOUR_ALG_CLASS::GetPMMLAlgorithmSAXHandler( 
    /* [in] */ IDMContextServices *in_pContext,
    /* [out] */ ISAXContentHandler **out_ppSAXHandler)
{ 
	HRESULT hr = S_OK;
	return hr; 
}